<template>
  <view :style="{backgroundColor:backgroundColor,padding:padding,borderRadius:radius}" class="tui-form__box">
    <slot></slot>
    <view v-if="showMessage"
          :class="{'tui-message__show':errorMsg}"
          :style="{top:tipTop+'px',padding:tipPadding,backgroundColor:getTipBgColor,borderRadius:tipRidus}"
          class="tui-form__errmsg">
      <text :style="{fontSize:tipSize+'rpx',color:tipColor}"
            class="tui-form__text">{{ errorMsg }}
      </text>
    </view>
    <view v-if="disabled" class="tui-form__mask"></view>
  </view>
</template>

<script>
import form from "./tui-validation.js"

export default {
  name: "tui-form",
  provide() {
    return {
      form: this
    }
  },
  props: {
    //表单数据对象
    model: {
      type: Object,
      default() {
        return {}
      }
    },
    //表单验证规则，即将废弃
    rules: {
      type: Array,
      default() {
        return []
      }
    },
    //表单背景颜色
    backgroundColor: {
      type: String,
      default: 'transparent'
    },
    //表单padding值
    padding: {
      type: String,
      default: '0'
    },
    //是否顶部弹出方式显示校验错误信息【为false时则可关联FormItem组件显示校验错误信息】
    showMessage: {
      type: Boolean,
      default: true
    },
    //表单圆角值
    radius: {
      type: String,
      default: '0'
    },
    //是否禁用该表单内的所有组件,透明遮罩层
    disabled: {
      type: Boolean,
      default: false
    },
    //提示框top值 px
    tipTop: {
      type: [Number, String],
      // #ifdef H5
      default: 44,
      // #endif
      // #ifndef H5
      default: 0
      // #endif
    },
    //错误提示框padding值
    tipPadding: {
      type: String,
      default: '20rpx'
    },
    //错误提示框背景色
    tipBackgroundColor: {
      type: String,
      default: ''
    },
    //错误提示字体大小
    tipSize: {
      type: [Number, String],
      default: 28
    },
    //错误提示字体颜色
    tipColor: {
      type: String,
      default: '#fff'
    },
    //错误提示框圆角值
    tipRidus: {
      type: String,
      default: '12rpx'
    },
    //错误消息显示时间 ms
    duration: {
      type: [Number, String],
      default: 0
    }
  },
  computed: {
    getTipBgColor() {
      return this.tipBackgroundColor || (uni && uni.$tui && uni.$tui.tuiForm.tipBackgroundColor) ||
          '#f74d54';
    }
  },
  watch: {
    showMessage(val) {
      if (this.children && this.children.length > 0) {
        this.children.forEach(item => {
          item.showError = val ? false : true
        })
      }
    }
  },
  data() {
    return {
      errorMsg: '',
      timer: null,
      formRules: [],
      isImmediate: false,
      concatRules: []
    };
  },
  created() {
    this.children = []
  },
  // #ifndef VUE3
  beforeDestroy() {
    this.clearTimer()
  },
  // #endif
  // #ifdef VUE3
  beforeUnmount() {
    this.clearTimer()
  },
  // #endif
  methods: {
    clearTimer() {
      clearTimeout(this.timer)
      this.timer = null;
      this.children = null
    },
    getFormItemRules() {
      let rules = []
      if (this.children && this.children.length > 0) {
        this.children.forEach(child => {
          let rule = child.getRules()
          rule && rules.push(rule)
        })
      }
      return rules;
    },
    getMergeRules(rules) {
      if (this.concatRules.length === 0) return rules;
      let formRules = [...rules]
      //合并并替换当前rules数据
      this.concatRules.forEach(item => {
        const index = rules.findIndex(e => e.name === item.name)
        if (index === -1) {
          formRules.push(item)
        } else {
          formRules[index] = item;
        }
      })
      return formRules;
    },
    //{Object} model 表单数据对象，传null则使用属性中model值
    //{Array} rules 表单验证规则，传null则使用FormItem项内传入的rules值
    //{Boolean} checkAll 是否返回所有错误信息
    validate(model, rules, checkAll = false) {
      model = model || this.model
      rules = rules || this.rules || []
      return new Promise((resolve, reject) => {
        try {
          if (rules.length === 0) {
            rules = this.getFormItemRules()
          } else {
            rules = this.getMergeRules(rules)
          }
          let res = form.validation(model, rules, checkAll);
          if (!res.isPass) {
            let errors = res.errorMsg;
            if (this.showMessage) {
              this.clearTimer()
              if (checkAll) {
                errors = errors[0].msg
              }
              this.errorMsg = errors;
              const duration = this.duration || (uni && uni.$tui && uni.$tui.tuiForm.duration) ||
                  2000
              this.timer = setTimeout(() => {
                this.errorMsg = ''
              }, Number(duration))
            } else {
              if (checkAll && this.children && this.children.length > 0) {
                //FormItem组件 显示提示
                this.children.forEach(item => {
                  const index = errors.findIndex(err => err.name === item.prop)
                  if (item.prop && item.prop !== true && ~index) {
                    item.errorMsg = errors[index].msg
                    item.itemValue = model[item.prop]
                  }
                })
              }
            }
          }
          resolve(res)
        } catch (e) {
          //TODO handle the exception
          reject({
            isPass: false,
            errorMsg: '校验出错，请检查数据格式是否有误！'
          })
        }
      })
    },
    //结合FormItem组件进行校验时 是否开启即时校验
    immediateValidate(isOpen, rules = []) {
      this.isImmediate = isOpen;
      if (isOpen) {
        if (!rules || rules.length === 0) {
          rules = this.getFormItemRules()
        } else {
          rules = this.getMergeRules(rules)
        }
        this.formRules = rules || []
      }
      if (this.children && this.children.length > 0) {
        this.children.forEach(item => {
          item.immediateValidate(isOpen)
        })
      }
    },
    //校验
    immediateValidator(prop, model, rules) {
      return new Promise((resolve, reject) => {
        try {
          let res = form.validation(model || this.model, rules || this.formRules, true);
          if (!res.isPass) {
            //显示提示
            let errors = res.errorMsg;
            const index = errors.findIndex(err => err.name === prop)
            if (~index) {
              res.errorMsg = errors[index].msg
            } else {
              res.isPass = true
              res.errorMsg = ''
            }
          }
          resolve(res)
        } catch (e) {
          reject({
            isPass: false,
            errorMsg: '校验出错，请检查数据格式是否有误！'
          })
        }
      })
    },
    clearValidate(props = []) {
      let arr = props;
      arr = !arr ? [] : arr
      if (typeof props === 'string') {
        arr = [props]
      }
      if (this.children && this.children.length > 0) {
        //清除指定字段的表单验证信息
        if (arr && arr.length > 0) {
          this.children.forEach(item => {
            if (item.prop && ~arr.indexOf(item.prop)) {
              item.errorMsg = ''
            }
          })
        } else {
          //清除所有字段的表单验证信息
          this.children.forEach(item => {
            item.errorMsg = ''
          })
        }
      }
    },
    /**
     * 验证具体的某个字段
     * @param {Array<string> ｜ String} props 字段key
     * @param {Array} rules 表单验证规则，当传null 或空数组时使用FormItem组件内rules
     * @param {Object} model 表单数据对象，不传则使用属性中model值
     */
    validateField(props, rules, model) {
      if (!rules || rules.length === 0) {
        rules = this.getFormItemRules()
      } else {
        rules = this.getMergeRules(rules)
      }
      const isString = typeof props === 'string';
      const formRules = rules.filter(item => props === item.name || (!isString && props
          .indexOf(item.name) !== -1));
      model = model || this.model
      return this.validate(model, formRules, true)
    },
    // 移除表单项
    uninstall(instance) {
      if (this.children && this.children.length > 0) {
        const index = this.children.findIndex(item => item === instance)
        if (index !== -1) {
          this.children.splice(index, 1)
        }
        const rules = instance.getRules() || {}
        const prop = instance.prop || rules.name || ''
        const idx = this.concatRules.findIndex(ru => ru.name === prop)
        if (idx !== -1) {
          this.concatRules.splice(idx, 1)
        }
      }
    }
  }
}
</script>

<style scoped>
.tui-form__box {
  /* #ifndef APP-NVUE */
  width: 100%;
  box-sizing: border-box;
  /* #endif */
  flex: 1;
  position: relative;
}

.tui-form__errmsg {
  position: fixed;
  z-index: 900;
  text-align: center;
  left: 20rpx;
  right: 20rpx;
  /* #ifndef APP-NVUE */
  box-sizing: border-box;
  display: flex;
  word-break: break-all;
  /* #endif */
  align-items: center;
  justify-content: center;
  padding: 24rpx;
  opacity: 0;
  transform: translateZ(0) translateY(-100%);
  transition-property: transform, opacity;
  transition-duration: 0.25s;
  transition-delay: 0s;
  transition-timing-function: ease-in-out;
}

.tui-form__text {
  text-align: center;
}

.tui-message__show {
  transform: translateY(0) translateZ(0);
  opacity: 1;
}

.tui-form__mask {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0);
  z-index: 99;
}
</style>
